#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
设备编码生成器 V2.0 - 重构版本

特性:
- 完善的数据库降级机制
- 事务安全的编码生成
- 完整的错误处理和监控
- 高并发场景支持
- 编码冲突检测和恢复
"""

import asyncio
import threading
import time
import uuid
from datetime import date, datetime
from queue import Queue, Empty
from typing import Dict, Any, Optional, List, Tuple
from enum import Enum
from dataclasses import dataclass

from common.database import DatabaseService
from common.logger import create_logger
from common.redis import RedisService, get_redis_service
from common.exceptions import BizException, ErrorCode
from app.common.code_persistence import CodeSerialPersistence

logger = create_logger(__name__)


class CodeGenerationMode(Enum):
    """编码生成模式"""
    REDIS_PRIMARY = "redis_primary"      # Redis主模式
    DATABASE_FALLBACK = "database_fallback"  # 数据库降级模式
    HYBRID = "hybrid"                    # 混合模式


class CodeGenerationError(Exception):
    """编码生成异常"""
    
    def __init__(self, message: str, error_code: str = None, cause: Exception = None):
        super().__init__(message)
        self.error_code = error_code or "CODE_GENERATION_ERROR"
        self.cause = cause


@dataclass
class CodeGenerationResult:
    """编码生成结果"""
    code: str
    sequence: int
    mode: CodeGenerationMode
    pool_key: str
    warning: bool = False
    message: Optional[str] = None


class DeviceCodeGeneratorV2:
    """设备编码生成器V2 - 重构版本"""
    
    # 编码规则常量
    STATION_PREFIX = "T"
    BOX_PREFIX = "B"
    RTU_PREFIX = "R"
    METER_PREFIX = "M"
    
    STATION_CODE_LEN = 15  # T + 9位区码 + 5位流水
    BOX_CODE_LEN = 19     # B + 台区码(14位) + 4位流水
    RTU_CODE_LEN = 16     # R + 9位区码 + 3位台区流水 + 3位RTU流水
    METER_CODE_LEN = 20   # M + 9位区码 + 3位台区流水 + 3位RTU流水 + 4位电表流水
    
    # 容量配置
    DAILY_POOL_SIZE = 99999      # 日级台区池容量
    DEVICE_POOL_SIZE = 9999      # 设备级池容量
    WARNING_THRESHOLD = 0.9       # 预警阈值
    
    # 缓存配置
    LOCAL_CACHE_SIZE = 100       # 本地缓存大小
    FETCH_BATCH_SIZE = 50        # 批量获取大小

    def __init__(self):
        self.redis_service = get_redis_service()
        self.mode = CodeGenerationMode.REDIS_PRIMARY
        self._local_caches: Dict[str, Queue] = {}
        self._cache_locks: Dict[str, threading.Lock] = {}
        self._fallback_counter = 0
        
    async def generate_station_code(self, db: DatabaseService, address_id: int) -> CodeGenerationResult:
        """
        生成台区编码: T + 9位区县码 + 5位日级流水
        
        Args:
            db: 数据库服务
            address_id: 地址ID
            
        Returns:
            编码生成结果
        """
        try:
            # 1. 获取区县码
            area_code = await self._get_area_code_from_address(db, address_id)
            
            # 2. 生成池键
            today = date.today().strftime("%Y%m%d")
            pool_key = f"station:{area_code}:{today}"
            
            # 3. 获取序列号
            sequence, mode, warning = await self._get_next_sequence_safe(
                db, pool_key, self.DAILY_POOL_SIZE, pool_type="STATION", 
                area_code=area_code, date_str=today
            )
            
            # 4. 组装编码
            station_code = f"{self.STATION_PREFIX}{area_code}{sequence:05d}"
            
            # 5. 验证编码唯一性
            await self._validate_code_uniqueness(db, station_code, "station")
            
            logger.info(f"生成台区编码成功: {station_code}, mode={mode.value}, sequence={sequence}")
            
            return CodeGenerationResult(
                code=station_code,
                sequence=sequence,
                mode=mode,
                pool_key=pool_key,
                warning=warning,
                message=f"台区编码生成成功，模式: {mode.value}"
            )
            
        except Exception as e:
            logger.exception(f"生成台区编码失败: address_id={address_id}")
            if isinstance(e, CodeGenerationError):
                raise
            raise CodeGenerationError(f"生成台区编码失败: {e}", cause=e)
    
    async def generate_box_code(self, station_code: str) -> CodeGenerationResult:
        """
        生成电箱编码: B + 台区码(去掉T) + 4位流水
        
        Args:
            station_code: 台区编码
            
        Returns:
            编码生成结果
        """
        try:
            if not self._validate_station_code_format(station_code):
                raise CodeGenerationError(f"无效的台区编码: {station_code}")
            
            # 生成池键
            pool_key = f"box:{station_code}"
            
            # 获取序列号
            sequence, mode, warning = await self._get_next_sequence_safe(
                None, pool_key, self.DEVICE_POOL_SIZE, pool_type="BOX",
                parent_code=station_code
            )
            
            # 组装编码
            box_code = f"{self.BOX_PREFIX}{station_code[1:]}{sequence:04d}"
            
            logger.info(f"生成电箱编码成功: {box_code}, mode={mode.value}, sequence={sequence}")
            
            return CodeGenerationResult(
                code=box_code,
                sequence=sequence,
                mode=mode,
                pool_key=pool_key,
                warning=warning
            )
            
        except Exception as e:
            logger.exception(f"生成电箱编码失败: station_code={station_code}")
            if isinstance(e, CodeGenerationError):
                raise
            raise CodeGenerationError(f"生成电箱编码失败: {e}", cause=e)
    
    async def generate_rtu_code(self, parent_code: str, parent_type: str) -> CodeGenerationResult:
        """
        生成RTU编码: R + 9位区县码 + 3位台区流水 + 3位RTU流水
        
        Args:
            parent_code: 父级设备编码（台区或电箱）
            parent_type: 父级类型 ("station" 或 "box")
            
        Returns:
            编码生成结果
        """
        try:
            # 解析父级编码获取基础信息
            area_code, station_seq = self._parse_parent_code_for_rtu(parent_code, parent_type)
            
            # 生成池键
            pool_key = f"rtu:{parent_code}"
            
            # 获取序列号
            sequence, mode, warning = await self._get_next_sequence_safe(
                None, pool_key, 999, pool_type="RTU",
                parent_code=parent_code
            )
            
            # 组装编码
            rtu_code = f"{self.RTU_PREFIX}{area_code}{station_seq}{sequence:03d}"
            
            logger.info(f"生成RTU编码成功: {rtu_code}, mode={mode.value}, sequence={sequence}")
            
            return CodeGenerationResult(
                code=rtu_code,
                sequence=sequence,
                mode=mode,
                pool_key=pool_key,
                warning=warning
            )
            
        except Exception as e:
            logger.exception(f"生成RTU编码失败: parent_code={parent_code}, parent_type={parent_type}")
            if isinstance(e, CodeGenerationError):
                raise
            raise CodeGenerationError(f"生成RTU编码失败: {e}", cause=e)
    
    async def generate_meter_code(self, rtu_code: str) -> CodeGenerationResult:
        """
        生成电表编码: M + 9位区县码 + 3位台区流水 + 3位RTU流水 + 4位电表流水
        
        Args:
            rtu_code: RTU编码
            
        Returns:
            编码生成结果
        """
        try:
            if not self._validate_rtu_code_format(rtu_code):
                raise CodeGenerationError(f"无效的RTU编码: {rtu_code}")
            
            # 从RTU码中提取信息
            area_code = rtu_code[1:10]      # 9位区县码
            station_seq = rtu_code[10:13]   # 3位台区流水
            rtu_seq = rtu_code[13:16]       # 3位RTU流水
            
            # 生成池键
            pool_key = f"meter:{rtu_code}"
            
            # 获取序列号
            sequence, mode, warning = await self._get_next_sequence_safe(
                None, pool_key, 9999, pool_type="METER",
                parent_code=rtu_code
            )
            
            # 组装编码
            meter_code = f"{self.METER_PREFIX}{area_code}{station_seq}{rtu_seq}{sequence:04d}"
            
            logger.info(f"生成电表编码成功: {meter_code}, mode={mode.value}, sequence={sequence}")
            
            return CodeGenerationResult(
                code=meter_code,
                sequence=sequence,
                mode=mode,
                pool_key=pool_key,
                warning=warning
            )
            
        except Exception as e:
            logger.exception(f"生成电表编码失败: rtu_code={rtu_code}")
            if isinstance(e, CodeGenerationError):
                raise
            raise CodeGenerationError(f"生成电表编码失败: {e}", cause=e)
    
    async def _get_next_sequence_safe(
        self, 
        db: Optional[DatabaseService], 
        pool_key: str, 
        max_value: int,
        pool_type: str,
        area_code: Optional[str] = None,
        parent_code: Optional[str] = None,
        date_str: Optional[str] = None
    ) -> Tuple[int, CodeGenerationMode, bool]:
        """
        安全获取下一个序列号，支持Redis和数据库双重保障
        
        Returns:
            (sequence, mode, warning) 元组
        """
        # 优先尝试Redis
        if self.redis_service.is_available():
            try:
                sequence, warning = await self._fetch_sequence_from_redis(pool_key, max_value)
                return sequence, CodeGenerationMode.REDIS_PRIMARY, warning
            except Exception as e:
                logger.warning(f"Redis获取序列号失败，降级到数据库: {pool_key}, error: {e}")
                self._fallback_counter += 1
        
        # 降级到数据库
        if db:
            try:
                sequence, warning = await self._fetch_sequence_from_database(
                    db, pool_key, max_value, pool_type, area_code, parent_code, date_str
                )
                return sequence, CodeGenerationMode.DATABASE_FALLBACK, warning
            except Exception as e:
                logger.error(f"数据库获取序列号失败: {pool_key}, error: {e}")
                raise CodeGenerationError(f"数据库降级失败: {e}", cause=e)
        
        # 最后的兜底方案：基于UUID的临时序列号
        logger.error(f"所有序列号获取方式失败，使用临时方案: {pool_key}")
        sequence = self._generate_emergency_sequence(max_value)
        return sequence, CodeGenerationMode.HYBRID, True
    
    async def _fetch_sequence_from_redis(self, pool_key: str, max_value: int) -> Tuple[int, bool]:
        """从Redis获取序列号"""
        lua_script = """
        local key = KEYS[1]
        local max_val = tonumber(ARGV[1])
        local warning_threshold = tonumber(ARGV[2])
        
        local current = redis.call('GET', key)
        if not current then
            current = 0
        else
            current = tonumber(current)
        end
        
        -- 检查是否超过上限
        if current >= max_val then
            return {-1, current, max_val}
        end
        
        -- 递增
        local new_val = redis.call('INCR', key)
        
        -- 检查预警
        local warning_flag = 0
        if new_val >= max_val * warning_threshold then
            warning_flag = 1
        end
        
        return {new_val, warning_flag}
        """
        
        try:
            result = self.redis_service.eval_lua_script(
                lua_script, [pool_key], [max_value, self.WARNING_THRESHOLD]
            )
            
            if result[0] == -1:
                raise CodeGenerationError(f"Redis编码池已满: {pool_key}, current={result[1]}, max={result[2]}")
            
            sequence = result[0]
            warning = result[1] == 1
            
            if warning:
                logger.warning(f"编码池使用率超过{self.WARNING_THRESHOLD*100}%: {pool_key}, current={sequence}, max={max_value}")
            
            return sequence, warning
            
        except Exception as e:
            logger.error(f"Redis Lua脚本执行失败: {pool_key}, error: {e}")
            raise
    
    async def _fetch_sequence_from_database(
        self, 
        db: DatabaseService, 
        pool_key: str, 
        max_value: int,
        pool_type: str,
        area_code: Optional[str] = None,
        parent_code: Optional[str] = None,
        date_str: Optional[str] = None
    ) -> Tuple[int, bool]:
        """从数据库获取序列号（完整的数据库降级实现）"""
        async with db.transaction():
            try:
                # 获取或创建池记录
                pool_info = await CodeSerialPersistence.get_pool_offset(db, pool_key)
                
                if not pool_info:
                    # 首次创建池
                    await CodeSerialPersistence.upsert_pool_offset(
                        db=db,
                        pool_name=pool_key,
                        pool_type=pool_type,
                        max_value=1,
                        pool_size=max_value,
                        area_code=area_code,
                        parent_code=parent_code,
                        date_str=date_str
                    )
                    sequence = 1
                    warning = False
                else:
                    # 更新现有池
                    current_max = pool_info["max_value"]
                    
                    if current_max >= max_value:
                        raise CodeGenerationError(f"数据库编码池已满: {pool_key}, current={current_max}, max={max_value}")
                    
                    new_max = current_max + 1
                    await CodeSerialPersistence.upsert_pool_offset(
                        db=db,
                        pool_name=pool_key,
                        pool_type=pool_type,
                        max_value=new_max,
                        pool_size=max_value,
                        area_code=area_code,
                        parent_code=parent_code,
                        date_str=date_str
                    )
                    
                    sequence = new_max
                    warning = sequence >= max_value * self.WARNING_THRESHOLD
                
                logger.info(f"数据库降级获取序列号成功: {pool_key}, sequence={sequence}")
                return sequence, warning
                
            except Exception as e:
                logger.error(f"数据库序列号获取失败: {pool_key}, error: {e}")
                raise
    
    def _generate_emergency_sequence(self, max_value: int) -> int:
        """生成紧急临时序列号"""
        # 基于时间戳和随机UUID生成相对唯一的序列号
        timestamp = int(time.time() * 1000) % 100000
        uuid_part = int(str(uuid.uuid4().int)[:6]) % 100000
        sequence = (timestamp + uuid_part) % max_value + 1
        
        logger.warning(f"使用紧急临时序列号: {sequence}")
        return sequence
    
    async def _validate_code_uniqueness(self, db: DatabaseService, code: str, device_type: str) -> None:
        """验证编码唯一性"""
        try:
            table_map = {
                "station": "dev_station",
                "box": "dev_box", 
                "rtu": "dev_rtu",
                "meter": "dev_meter"
            }
            
            table = table_map.get(device_type)
            if not table:
                return
            
            sql = f"SELECT id FROM {table} WHERE code = %s AND deleted = 0 LIMIT 1"
            existing = db.get_one(sql, (code,))
            
            if existing:
                raise CodeGenerationError(f"编码冲突: {code} 已存在于 {table}")
                
        except Exception as e:
            if isinstance(e, CodeGenerationError):
                raise
            logger.warning(f"编码唯一性检查失败: {code}, device_type={device_type}, error: {e}")
    
    async def _get_area_code_from_address(self, db: DatabaseService, address_id: int) -> str:
        """通过address_id获取9位区县码"""
        try:
            sql = """
                SELECT region_code, level, name, full_name
                FROM base_region_address
                WHERE id = %s AND is_active = 1
            """
            address = db.get_one(sql, (address_id,))
            
            if not address:
                raise CodeGenerationError(f"地址ID {address_id} 不存在或已停用")
            
            region_code = address.get("region_code")
            if not region_code:
                raise CodeGenerationError(f"地址ID {address_id} 缺少区域码")
            
            # 确保是9位区县码格式
            if len(region_code) == 6:
                area_code = region_code + "000"
            elif len(region_code) >= 9:
                area_code = region_code[:9]
            else:
                raise CodeGenerationError(f"地址ID {address_id} 区域码格式无效: {region_code}")
            
            if not self._validate_area_code(area_code):
                raise CodeGenerationError(f"区县码格式无效: {area_code}")
            
            return area_code
            
        except Exception as e:
            if isinstance(e, CodeGenerationError):
                raise
            logger.exception(f"获取区县码失败: address_id={address_id}")
            raise CodeGenerationError(f"获取区县码失败: {e}", cause=e)
    
    def _parse_parent_code_for_rtu(self, parent_code: str, parent_type: str) -> Tuple[str, str]:
        """解析父级编码获取RTU编码所需信息"""
        if parent_type == "box":
            if not self._validate_box_code_format(parent_code):
                raise CodeGenerationError(f"无效的电箱编码: {parent_code}")
            # 电箱编码: B + 台区码(14位) + 4位流水
            area_code = parent_code[1:10]      # 9位区县码
            station_seq = parent_code[10:13]   # 3位台区流水（取前3位）
            
        elif parent_type == "station":
            if not self._validate_station_code_format(parent_code):
                raise CodeGenerationError(f"无效的台区编码: {parent_code}")
            # 台区编码: T + 9位区县码 + 5位流水
            area_code = parent_code[1:10]      # 9位区县码
            station_seq = parent_code[10:13]   # 5位台区流水，取前3位
            
        else:
            raise CodeGenerationError(f"无效的父级类型: {parent_type}")
        
        return area_code, station_seq
    
    @staticmethod
    def _validate_area_code(area_code: str) -> bool:
        """验证区县码是否符合GB/T 2260标准"""
        if not area_code or len(area_code) != 9 or not area_code.isdigit():
            return False
        
        # 基本格式检查：省市区都不能为00
        province = area_code[:2]
        city = area_code[2:4]
        area = area_code[4:6]
        
        return province != "00" and city != "00" and area != "00"
    
    @staticmethod
    def _validate_station_code_format(code: str) -> bool:
        """验证台区编码格式"""
        return (code and len(code) == 15 and code.startswith("T") and 
                code[1:10].isdigit() and code[10:15].isdigit())
    
    @staticmethod
    def _validate_box_code_format(code: str) -> bool:
        """验证电箱编码格式"""
        return (code and len(code) == 19 and code.startswith("B") and 
                code[1:15].isdigit() and code[15:19].isdigit())
    
    @staticmethod
    def _validate_rtu_code_format(code: str) -> bool:
        """验证RTU编码格式"""
        return (code and len(code) == 16 and code.startswith("R") and 
                code[1:10].isdigit() and code[10:13].isdigit() and code[13:16].isdigit())


# 全局实例
_device_code_generator_v2 = None


def get_device_code_generator_v2() -> DeviceCodeGeneratorV2:
    """获取设备编码生成器V2单例"""
    global _device_code_generator_v2
    if _device_code_generator_v2 is None:
        _device_code_generator_v2 = DeviceCodeGeneratorV2()
    return _device_code_generator_v2